import struct


def swap16(value):
    """交换16位整数的字节顺序"""
    return ((value & 0xFF) << 8) | ((value >> 8) & 0xFF)


def rgb888_to_rgb565(r, g, b):
    """RGB888转RGB565"""
    RGB565 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
    return swap16(RGB565)


common = """// RLE16压缩数据格式说明：
// 只有游程长度大于2时才压缩为run（0x8xxx,像素），否则为literal（0x0xxx,像素...）。
// 其中xxx为长度，像素为RGB565格式。
"""


def bmp_rle16_to_carray(bmp_path, array_name, out_path):
    """
    读取24位BMP文件，使用RLE16压缩并输出C语言格式数组。
    压缩格式说明：
      - 只有游程长度大于2时才压缩为run（高位1，低15位为长度，后跟像素值）;
      - 其它情况为literal（高位0，低15位为长度，后跟若干像素值）;
    """

    with open(bmp_path, 'rb') as f_bmp, open(out_path, 'w') as f_out:
        # 读取BMP头
        header = f_bmp.read(54)
        width = struct.unpack('<I', header[18:22])[0]
        height = struct.unpack('<I', header[22:26])[0]
        bpp = struct.unpack('<H', header[28:30])[0]
        if bpp != 24:
            raise ValueError("仅支持24位BMP")
        offset = struct.unpack('<I', header[10:14])[0]
        f_bmp.seek(offset)
        row_padded = ((width * 3 + 3) & ~3)

        # 读取所有像素（BMP底到顶，需反序）
        rows = []
        for _ in range(height):
            data = f_bmp.read(row_padded)
            row = []
            for i in range(0, width * 3, 3):
                b, g, r = data[i], data[i+1], data[i+2]
                row.append(rgb888_to_rgb565(r, g, b))
            rows.append(row)
        pixels = [pix for row in reversed(rows) for pix in row]
        n = len(pixels)

        # RLE压缩
        f_out.write(common)
        f_out.write(f"// Width: {width}, Height: {height}\n")
        f_out.write(f"const unsigned short {array_name}[] = \n{{")
        rle_count = 0
        line_count = 0
        i = 0
        while i < n:
            run_val = pixels[i]
            run_len = 1

            # 只有run_len>2才压缩为run
            while i + run_len < n and pixels[i + run_len] == run_val and run_len < 0x7FFF:
                run_len += 1

            if run_len > 2:
                # 输出重复(run)
                f_out.write(
                    f"\n /* RLE:{run_len:5d} @ {(i%width):3d},{(i//width):3d} */"
                    f" 0x8000U + {run_len}, 0x{run_val:04X}, ")
                rle_count += 2
                line_count += 2
                i += run_len
            else:
                # 输出原始(literal)
                lit_start = i
                lit_len = 1
                while (lit_start + lit_len < n):
                    next_run_val = pixels[lit_start + lit_len]
                    next_run_len = 1
                    while (lit_start + lit_len + next_run_len < n and
                           pixels[lit_start + lit_len + next_run_len] == next_run_val and next_run_len < 0x7FFF):
                        next_run_len += 1
                    # 如果后面有长度大于2的run，则结束literal
                    if next_run_len > 2:
                        break
                    lit_len += 1
                f_out.write(
                    f"\n /* ABS:{lit_len:5d} @ {(i%width):3d},{(i//width):3d} */ {lit_len:5d} , ")
                for j in range(lit_len):
                    f_out.write(f"0x{pixels[lit_start + j]:04X}, ")
                rle_count += 1 + lit_len
                line_count += 1 + lit_len
                i += lit_len
        f_out.write("\n};\n")
        f_out.write(
            f"// RLE16 length: {rle_count}, total {rle_count*2} bytes for {n} pixels. \n")
        f_out.write("// EOF //\n")


if __name__ == "__main__":
    bmp_file = "LOGO-320.bmp"  # 修改为你的BMP文件路径
    array_name = "image_rle16"
    out_file = "output.c"
    bmp_rle16_to_carray(bmp_file, array_name, out_file)
    print("C数组已生成到 output.c")
